
;window.ServiceHelper = (function (factory) {
  var
    isInit = false,
    completeInit = 0,
    authToken = null,
    apiPath = null,
    cmdSeparator = '/',
    tokenFieldName = 'asc_auth_key',
    observerHandler = 0,
    observerTimeout = 500,
    maxRequestAttempts = 3,
    requestTimeout = 60 * 1000,
    myProfile = null,
    portalSettings = null,
    localStorage = window.localStorage || {},
    customEvents = {},
    requests = [];

  function isArray (o) {
   return o.constructor.toString().indexOf("Array") != -1;
  }

  function getRandomId (prefix) {
    return '' + (prefix ? prefix + '-' : '') + Math.floor(Math.random() * 1000000);
  }

  function getUniqueId (o, prefix) {
    var 
      iterCount = 0,
      maxIterations = 1000,
      uniqueId = getRandomId(prefix);
    while (o.hasOwnProperty(uniqueId) && iterCount++ < maxIterations) {
      uniqueId = getRandomId(prefix);
    }
    return uniqueId;
  }

  function canAddRequest (req, requests) {
    var item = null;
    if (req.___once === true) {
      var requestsInd = requests.length;
      while (requestsInd--) {
        item = requests[requestsInd];
        if (item.___eventname === req.___eventname && item.type === req.type && item.url === req.url) {
          return false;
        }
      }
    }
    return true;
  }

  function getUrl () {
    var url = '', cmds = [];
    for (var i = 0, n = arguments.length; i < n; i++) {
      cmds.push(arguments[i]);
    }
    url = cmds.join(cmdSeparator);
    //console.log(apiPath + (url.charAt(0) === cmdSeparator ? url.substring(1) : url))
    return apiPath + (url.charAt(0) === cmdSeparator ? url.substring(1) : url);
  }

  function beforeSend (jqXHR, settings) {
    if (authToken) {
      //jqXHR.setRequestHeader('Cookie', authToken);
      var url = settings.url;
      settings.url = url + (url.indexOf('?') === -1 ? '?' : '&') + tokenFieldName + '=' + encodeURIComponent(authToken);
    }
  }

  function requestSuccess (req, eventname, params, responses, jqXHR, textStatus) {
    var obj = responses && req.___singleresponse === true ? factory.create(req.url, responses[0]) : null;
    if (obj) {
      if (req.___isme === true) {
        onGetMe(req.___params, obj);
      }
      if (req.___isauth === true) {
        onGetAuthentication(req.___params, obj);
      }
      if (req.___issettings === true) {
        onGetSettings(req.___params, obj);
      }
      var needexec = true;
      if (typeof req.___callback === 'function') {
        if (req.___callback(req.___params, obj) === false) {
          needexec = false;
        }
      }
      if (needexec === true) {
        exec(null, this, [req.___params, obj]);
        if (req.___eventname) {
          exec(req.___eventname, this, [req.___params, obj]);
        }
      }
    }

    if (obj === null && responses && req.___singleresponse === false) {
      var args = [req.___params], IsValidData = true;
      for (var i = 0, n = responses.length; i < n; i++) {
        obj = factory.create(req.___urls[i], responses[i], responses);
        obj ? args.push(obj) : IsValidData = false;
      }
      if (IsValidData) {
        var needexec = true;
        if (typeof req.___callback === 'function') {
          if (req.___callback.apply(this, args) === false) {
            needexec = false;
          }
        }
        if (needexec === true) {
          exec(null, this, args);
          if (req.___eventname) {
            exec(req.___eventname, this, args);
          }
        }
      }
    }
  }

  function requestComplete (jqXHR, textStatus) {
    var
      errorMsg = null,
      isInvalidRequest = false,
      isUnauthenticationRequest = false,
      response = null,
      req = requests[0];

    isInvalidRequest = false;
    isUnauthenticationRequest = false;
    switch (textStatus) {
      case 'error' :
        req.___errcount++;
        isInvalidRequest = true;
        errorMsg = 'throw error';
        break;
      case 'timeout' :
        req.___totcount++;
        isInvalidRequest = true;
        errorMsg = 'timeout expected';
        break;
      case 'parsererror' :
        isInvalidRequest = true;
        errorMsg = 'parse error';
        break;
    }

    if (jqXHR.isRejected()) {
      isInvalidRequest = true;
      isUnauthenticationRequest = true;
      errorMsg = 'rejected request';
    }

    if (!isInvalidRequest) {
      try {
        response = jQuery.parseJSON(jqXHR.responseText);
      } catch (err) {
        response = null;
        isInvalidRequest = true;
        errorMsg = 'response parse error';
      }
    }

    if (!isInvalidRequest) {
      if (response && response.status != 0) {
        isInvalidRequest = true;
        errorMsg = 'invalid status';
      }
    }

    if (isInvalidRequest) {
      if (errorMsg) {
        req.___errors.push(errorMsg);
      }
      if (isUnauthenticationRequest) {
        exec('unauthenticated', this, []);
      }
      req.___processing = false;
      delete req;
      return undefined;
    }

    var req = requests[0].___processing === true ? requests[0] : null;
    if (!req) {
      return undefined;
    }

    req.___urls.push(req.url);
    req.___responses.push(response.response);

    if (req.batch.length > 0) {
      delete req.___errors;
      req.___errors = null;
      req.___errors = [];
      req.___errcount = 0;
      req.___totcount = 0;
      req.___attcount = 0;
      req.___processing = false;
      req.url = req.batch[0];
      req.batch = req.batch.slice(1);
      return undefined;
    }

    var args = [req, req.___eventname, req.___params, req.___responses];
    for (var i = 0, n = arguments.length; i < n; i++) {
      args.push(arguments[i]);
    }
    requestSuccess.apply(this, args);

    var requestsInd = requests.length;
    while (requestsInd--) {
      if (requests[requestsInd] === req) {
        clearRequest(requests[requestsInd]);
        delete requests[requestsInd];
        requests[requestsInd] = null;
        requests.splice(requestsInd, 1);
      }
    }
    delete req;
  }

  function requestsObserver () {
    if (requests.length > 0 && requests[0].___processing === false) {
      var req = null;
      req = requests[0];
      if (req.___attcount >= maxRequestAttempts) {
        exec('extention', this, [req.___eventname, req.___params, req.___errors]);
        var requestsInd = requests.length;
        while (requestsInd--) {
          if (requests[requestsInd] === req) {
            clearRequest(requests[requestsInd]);
            delete requests[requestsInd];
            requests[requestsInd] = null;
            requests.splice(requestsInd, 1);
          }
        }
        delete req;
        return undefined;
      }

      if ((authToken && completeInit === 3) || (authToken && requests[0].___isme === true) || (authToken && requests[0].___issettings === true) || requests[0].___isauth === true) {
        sendRequest(requests[0]);
      }
    }
  }

  function clearRequest (req) {
    for (var fld in req) {
      delete req[fld];
      req[fld] = null;
    }
  }

  function createRequest (eventname, params, type, url, data, callback) {
    url = typeof url === 'string' ? getUrl(url) : url;
    if (isArray(url)) {
      for (var i = 0, n = url.length; i < n; i++) {
        url[i] = getUrl(url[i]);
      }
    }

    return {
      ___errors : [],
      ___errcount : 0,
      ___totcount : 0,
      ___attcount : 0,
      ___eventname : eventname || '',
      ___processing : false,
      ___params : params,
      ___once : params.hasOwnProperty('___once') ? params.___once === true : false,
      ___callback : typeof callback === 'function' ? callback : null,
      ___isauth : eventname === 'authentication',
      ___issettings : eventname === 'settings',
      ___isme : eventname === 'me',
      ___urls : [],
      ___responses : [],
      ___singleresponse : typeof url === 'string',
      beforeSend : beforeSend,
      jsonp : false,
      async : true,
      dataType : 'json',
      // contentType : contentType,
      cache : false,
      //converters: {'* text' : true, 'text script' : true, 'text html' : true, 'text json' : false, 'text xml' : true},
      url : typeof url === 'string' ? url : url[0],
      batch : typeof url === 'object' && url.length > 1 ? url.slice(1) : [],
      type : type,
      data : data,
      timeout : requestTimeout,
      complete : requestComplete
    };
  }

  function sendRequest (req) {
    req.___attcount++;
    req.___processing = true;
    jQuery.ajax(req);
  }

  function addRequest (eventname, params, type, url, data, callback) {
    params = params || {};
    var req = createRequest(eventname, params, type, url, data, callback);
    if (canAddRequest(req, requests)) {
      requests.push(req);
    }
  }

  function firstRequest (eventname, params, type, url, data, callback) {
    var req = createRequest(eventname, params, type, url, data, callback);
    if (canAddRequest(req, requests)) {
      requests.unshift(createRequest(eventname, params, type, url, data, callback));
    }
  }

  function onGetMe (params, obj) {
    completeInit |= 1;
    myProfile = obj;

    if (completeInit === 3) {
      exec('completeinit', this, [myProfile, portalSettings]);
    }
  }

  function onGetSettings (params, obj) {
    completeInit |= 2;
    portalSettings = obj;

    if (completeInit === 3) {
      exec('completeinit', this, [myProfile, portalSettings]);
    }
  }

  function onGetAuthentication (params, obj) {
    authToken = obj.token;
    localStorage[tokenFieldName] = authToken;
    ASC.Controls.cookies.set(tokenFieldName, authToken);

    settings({___once : true});
    me({___once : true});
  }

  var init = function (apipath) {
    if (isInit === true) {
      return undefined;
    }
    isInit = true;

    apiPath = typeof apipath === 'string' && apipath.length > 0 ? apipath : apiPath;
    if (apiPath) {
      apiPath = apiPath.charAt(apiPath.length - 1) === '/' ? apiPath : apiPath + '/';
    }

    observerHandler = setInterval(requestsObserver, observerTimeout);

    var token = localStorage[tokenFieldName] || null;
    token = token ? token : ASC.Controls.cookies.get(tokenFieldName) || null;

    authToken = token ? token : authToken;

    settings({___once : true});
    me({___once : true});
  };

  var bind = function (eventname, handler) {
    if (!customEvents.hasOwnProperty(eventname)) {
      customEvents[eventname] = [];
    }
    if (typeof handler === 'function') {
      customEvents[eventname].push(handler);
    }
  };

  var exec = function (eventname, obj, args) {
    if (customEvents.hasOwnProperty(eventname)) {
      var handlers = customEvents[eventname];
      obj = obj || window;
      args = args || [];
      for (var i = 0, n = handlers.length; i < n; i++) {
        handlers[i].apply(obj, args);
      }
    }
  };

  var me = function (params, callback) {
    params = params || {};

    firstRequest(
      'me',
      params,
      'get',
      'people/@self.json',
      null,
      callback
    );
  };

  var settings = function (params, callback) {
    params = params || {};

    firstRequest(
      'settings',
      params,
      'get',
      'settings.json',
      null,
      callback
    );
  };

  var login = function (params, username, password, callback) {
    params = params || {};

    if (!apiPath) {
      return false;
    }

    firstRequest(
      'authentication',
      params,
      'post',
      'authentication.json',
      {username : username, password : password},
      callback
    );
    return true;
  };

  var logged = function () {
    if (authToken) {
      return true;
    }
    exec('unauthenticated', this, []);
    return false;
  };

  var test = function () {
    
  };
  return {
    test    : test,
    init    : init,
    bind    : bind,
    exec    : exec,
    login   : login,
    logged  : logged,
    request : addRequest
  }
})(ServiceFactory);
